/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#pragma once

struct ElementSize
{
	ElementSize()
		:minimum(0), preferred(0) {}
	ElementSize(int minimum, int preferred)
		:minimum(minimum), preferred(preferred) {}
	int minimum;
	int preferred;
};

/////////////////////////////////////////////////////////////////////////////

struct ElementContext
{
	ElementContext();
	ElementContext(ScreenbarWindow *window, Monitor *monitor, VirtualDesktop *desk, HWND task, bool vertical);
	
	WindowData *getTask() const;
	
	bool operator<(const ElementContext &rhs) const;
	
	/// True ifef all fields match, except boundingRect and hovered, which may differ
	bool match(const ElementContext &rhs) const;
	
	ScreenbarWindow *window;
	Monitor *monitor;
	VirtualDesktop *desk;
	HWND taskWindow;
	
	int thickness;
	bool vertical;

	bool hovered;
};
extern LayoutReference *clickContext;

struct LayoutLocation
{
	LayoutElement *element;
	ElementContext context;
	
	bool operator<(const LayoutLocation &rhs) const;
	bool operator==(const LayoutLocation &rhs) const;
};

/////////////////////////////////////////////////////////////////////////////

class Layout
{
public:
	Layout(LayoutElement *rootElement, ElementContext rootContext, Rect boundingRect, string sizeFallbacks);
	~Layout();
	
	void setRect(Rect boundingRect);
	bool update();
	bool updateHover();
	void draw(HDC drawContext);
	void traverse(vector<LayoutCacheNode*> &visited);
	ElementSize getLength();
	
	LayoutReference *elementAtPoint(int x, int y);
	LayoutReference *getReferenceTo(LayoutCacheNode *node);
	LayoutReference *findNodeMatching(bool (*condition)(LayoutCacheNode*, void*), void *param);
	bool allowAutoHide();
	bool isAnimating();
	
	void printTree();
	
protected:
	friend class LayoutReference;
	
	LayoutCacheNode *buildElement(LayoutElement *element, const ElementContext *context, SizeFallback *fallback);
	void deleteTree(LayoutCacheNode *node);
	bool updateTree(LayoutCacheNode *node, SizeFallback *fallback);
	bool matchNodes(LayoutCacheNode *a, LayoutLocation *b);
	void computeLengths(LayoutCacheNode *node);
	void computePositions(LayoutCacheNode *node);
	bool checkHover(LayoutCacheNode *node, Point cursorPos, SizeFallback *fallback);
	
	void printTree(LayoutCacheNode *node, int indentLevel);
	
	bool fallbackFits(int index);
	
	LayoutElement *rootElement;
	ElementContext rootContext;
	Rect boundingRect;
	
	LayoutCacheNode *cachedRoot;
	LayoutCacheNode *prevFallback;
	vector<LayoutCacheNode*> hoveredNodes;
	bool animating;
	
	int fallbackUsed;
	SizeFallbackChain *sizeFallbacks;
	vector<LayoutReference*> references;
};

/////////////////////////////////////////////////////////////////////////////

class LayoutReference
{
public:
	LayoutReference(Layout *layout, LayoutCacheNode *node);
	LayoutReference(const LayoutReference &copy);
	~LayoutReference();
	
	LayoutCacheNode *getNode();
	Layout *getLayout();
	
	void invalidate();
	
protected:
	Layout *layout;
	LayoutCacheNode *node;
};

/////////////////////////////////////////////////////////////////////////////

class LayoutElement
{
public:
	LayoutElement(string prefix);
	virtual ~LayoutElement();
	
	/// Returns true if this element has itself been changed. Changes to
	/// children (that is, changes which change the return value of
	/// getChildren), or changes to layout caused by changing sizes of
	/// children, don't count.
	virtual bool changed(const LayoutCacheNode *node);
	
	virtual void update(LayoutCacheNode *node);
	
	virtual void deleteElementData(LayoutCacheNode *node, void *elementData);
	
	/// Returns the minimum (first) and preferred (second) length of this
	/// element. In flow layouts, size along one axis (thickness) is fixed
	/// while size on the other axis (length) may vary. Layout may be built
	/// with length less than preferred because total length is limited by the
	/// size of the screen. prevLayout is optional; if nonnull, the sizes of
	/// children will be taken from it, rather than recalculated.
	virtual ElementSize getLength(const LayoutCacheNode *node);
	
	/// Get a list of this element's children and their contexts.
	virtual void getChildren(LayoutCacheNode *node, vector<LayoutLocation> &outChildren);
	
	/// True if calling getChildren() can return different results for the same
	/// element with matching contexts
	bool childrenCanChange;
	
	/// True if this element is animating
	bool animating;
	
	/// Given a layout node for a container, which has the correct bounding
	/// rect, fill in the bounding rects for children. The children are layout
	/// cache nodes constructed from the return value of getChildren, and are
	/// complete except for their bounding rects.
	virtual void arrangeChildren(LayoutCacheNode *container, vector<LayoutCacheNode*> &children);
	
	/// Draw this element on the given surface, in the context and at the
	/// location specfied by node.
	virtual void draw(HDC drawContext, const LayoutCacheNode *node);
	
	/// Determine whether the mouse cursor is hovering over this element
	bool isHovered(LayoutCacheNode *node);
	
	/// Whether a panel that this element is on is allowed to auto-hide
	/// itself. False for pressed start buttons, true otherwise.
	virtual bool allowAutoHide(LayoutCacheNode *node);
	
	virtual bool isExpanding();
	string toString() const;
	
	virtual void dropOn(LayoutCacheNode *node, DragItem *item);
	virtual void hoverDragged(LayoutCacheNode *node, DragItem *item);
	
	string prefix;
	
	string onPress[mouseNumButtons];
	string onRelease[mouseNumButtons];
	string onClick[mouseNumButtons];
	string toolTip;
	bool draggable;
	bool isMinimizeTarget;
	LayoutElement *dragElement;
	LayoutElement *dragPlaceholder;
	
protected:
	friend class Layout;
	
	bool hoverGroup;
};

class SubstituteElement
	:public LayoutElement
{
public:
	SubstituteElement(LayoutElement *child);
	LayoutElement *removeSubstitute();
	
protected:
	LayoutElement *child;
};

/////////////////////////////////////////////////////////////////////////////

class LayoutPool
{
public:
	LayoutPool();
	~LayoutPool();
	
	void parseElementList(const string &elements, vector<LayoutElement*> *elementList);
	LayoutElement *getElement(string prefix);
	LayoutElement *getSpacerElement(int size, bool optional, bool expanding);
	
protected:
	LayoutElement *createElement(string prefix);
	map<string, LayoutElement*> cachedElements;
	vector<LayoutElement*> elements;
	
	// The set of elements whose constructors are running right now. Used to
	// detect circular dependencies.
	set<string> constructingElements;
};
extern LayoutPool *layoutPool;

